package com.pan.admin.module.api.service.impl;

import com.gitee.apanlh.util.algorithm.KeyUtils;
import com.gitee.apanlh.util.base.BigDecimalUtils;
import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.IteratorUtils;
import com.gitee.apanlh.util.id.IdUtils;
import com.gitee.apanlh.util.reflection.CopyUtils;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;
import com.pan.admin.common.annotation.DataSource;
import com.pan.admin.common.constant.HttpStatus;
import com.pan.admin.common.enums.BalanceOperationType;
import com.pan.admin.common.enums.DataSourceType;
import com.pan.admin.common.exception.ServiceException;
import com.pan.admin.common.transaction.TransactionSupport;
import com.pan.admin.common.utils.DateUtils;
import com.pan.admin.common.utils.SecurityUtils;
import com.pan.admin.common.utils.StringUtils;
import com.pan.admin.module.api.dao.ApiKeyDao;
import com.pan.admin.module.api.dao.ApiRequiredRuleDao;
import com.pan.admin.module.api.entity.ApiKey;
import com.pan.admin.module.api.entity.ApiRequiredRule;
import com.pan.admin.module.api.entity.po.ApiKeyBillPO;
import com.pan.admin.module.api.entity.vo.ApiKeyBalanceOperationVO;
import com.pan.admin.module.api.entity.vo.ReqApiKeyVO;
import com.pan.admin.module.api.lock.ApiKeyBalanceOperationLock;
import com.pan.admin.module.api.service.ApiKeyBillService;
import com.pan.admin.module.api.service.ApiKeyService;
import com.pan.admin.module.api.service.ApiRequiredRuleService;
import com.pan.admin.module.channel.dao.LoanChannelDao;
import com.pan.admin.module.channel.entity.LoanChannel;
import com.ts.cache.apikey.ApiKeyCacheService;
import com.ts.cache.apikey.ApiKeyRuleCacheService;
import com.ts.cache.apikey.entity.ApiKeyCache;
import com.ts.cache.apikey.entity.ApiKeyListRuleCache;
import com.ts.cache.apikey.entity.ApiKeyRuleCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 *  keysService业务层处理
 *
 *  @author ts
 *  @date 2023-10-26
 */
@Service
public class ApiKeyServiceImpl implements ApiKeyService {

    @Autowired
    private ApiKeyBalanceOperationLock apiKeyBalanceOperationLock;
    @Autowired
    private TransactionSupport transactionSupport;
    @Autowired
    private ApiKeyDao apiKeyDao;
    @Autowired
    private LoanChannelDao loanChannelDao;
    @Autowired
    private ApiRequiredRuleDao apiRequiredRuleDao;
    @Autowired
    private ApiRequiredRuleService apiRequiredRuleService;
    @Autowired
    private ApiKeyBillService apiKeyBillService;
    @Autowired
    private ApiKeyCacheService apiKeyCacheService;
    @Autowired
    private ApiKeyRuleCacheService apiKeyRuleCacheService;

    @Override
    @Transactional(readOnly = true)
    public List<Map<String, Object>> listApiKey(String keyword) {
        return this.apiKeyDao.listApiKey(keyword);
    }

    /**
     * 根据ID获取列表keys
     *
     * @param id keys主键
     * @return keys
     */
    @Override
    public ApiKey getById(Long id) {
        return apiKeyDao.getById(id);
    }

    /**
     * 查询keys列表
     *
     * @param apiKeys keys
     * @return keys
     */
    @Override
    public List<ApiKey> list(ApiKey apiKeys) {
        return apiKeyDao.list(apiKeys);
    }

    /**
     * 查询keys列表
     *
     * @param apiKeys keys
     * @return keys
     */
    @Override
    @Transactional(readOnly = true)
    @DataSource(DataSourceType.SLAVE)
    public List<ApiKey> listSlave(ApiKey apiKeys) {
        return apiKeyDao.list(apiKeys);
    }

    /**
     * 新增keys
     *
     * @param reqApiKeyVO
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertApiKeys(ReqApiKeyVO reqApiKeyVO) {
        //  apiKey实体类数据
        ApiKey apiKey = reqApiKeyVO.getApiKey();
        String apiClientId = IdUtils.snowFlakeIdStr();
        int row;

        //  新增渠道
        LoanChannel loanChannel = new LoanChannel();
        loanChannel.setUniqueCode(apiClientId);
        loanChannel.setType(3);
        loanChannel.setVersion(10);
        loanChannel.setProviderType(apiKey.getApiAccessProviderType());
        loanChannel.setHistoryUserProportion(apiKey.getApiHistoryUserProportion());
        loanChannel.setHistoryUserOrderType(apiKey.getApiHistoryUserOrderType());
        loanChannel.setName("API渠道-" + apiKey.getApiName());
        loanChannel.setCreateTime(DateUtils.getNowDate());
        loanChannel.setCreateBy(SecurityUtils.getUsername());
        row = loanChannelDao.insertLoanChannel(loanChannel);

        //  设置API密钥管理值返回渠道新增id
        apiKey.setApiClientId(apiClientId);
        apiKey.setApiSecretKey(KeyUtils.generate256ToHexStr());
        apiKey.setCreateTime(DateUtils.getNowDate());
        apiKey.setStatus(1);
        apiKey.setLoanChannelId(loanChannel.getId());

        //  新增API密钥管理
        row = apiKeyDao.insertApiKeys(apiKey);

        //  新增进件必填项
        List<Long> optionsFormSelected = reqApiKeyVO.getOptionsFormSelected();
        if (!ValidParam.isEmpty(optionsFormSelected)) {
            List<ApiRequiredRule> apiRequiredRules = margeRule(apiClientId, 1, optionsFormSelected);
            for (ApiRequiredRule apiRequiredRule : apiRequiredRules){
                apiRequiredRule.setCreateTime(DateUtils.getNowDate());
            }
            apiRequiredRuleDao.batchInsert(apiRequiredRules);
        }

        //  新增撞库必填项
        List<Long> optionsFormBumpSelected = reqApiKeyVO.getOptionsFormBumpSelected();
        if (!ValidParam.isEmpty(optionsFormBumpSelected)) {
            List<ApiRequiredRule> apiRequiredRules = margeRule(apiClientId, 0, optionsFormBumpSelected);
            for (ApiRequiredRule apiRequiredRule : apiRequiredRules){
                apiRequiredRule.setCreateTime(DateUtils.getNowDate());
            }
            apiRequiredRuleDao.batchInsert(apiRequiredRules);
        }

        if (row < 1) {
            throw new ServiceException("回滚");
        }

        //  将API更新存入缓存
        ApiKeyCache apiKeyCache = CopyUtils.copy(apiKey, ApiKeyCache.class);
        apiKeyCacheService.set(apiKey.getApiClientId(), apiKeyCache);

        //  API规则存入缓存
        apiKeyRuleCacheService.reload(() -> {
            //  获取对应规则表单信息
            ApiRequiredRule apiRequiredRule = new ApiRequiredRule();
            apiRequiredRule.setApiClientId(apiKeyCache.getApiClientId());
            List<ApiRequiredRule> listRuleForm = apiRequiredRuleDao.listAndForm(apiRequiredRule);
            Map<Integer, List<ApiRequiredRule>> ruleTypeMap = CollUtils.groupBy(listRuleForm, t -> t.getType());
            List<ApiKeyRuleCache> listDataExchangeRule = CopyUtils.copyList(ruleTypeMap.get(0), ApiKeyRuleCache.class);
            List<ApiKeyRuleCache> listApplyRule = CopyUtils.copyList(ruleTypeMap.get(1), ApiKeyRuleCache.class);

            //  保存redis信息
            ApiKeyListRuleCache apiKeyListRuleCache = new ApiKeyListRuleCache();
            apiKeyListRuleCache.setApiClientId(apiKeyCache.getApiClientId());
            apiKeyListRuleCache.setListDataExchangeRule(listDataExchangeRule);
            apiKeyListRuleCache.setListApplyRule(listApplyRule);
            return apiKeyListRuleCache;
        });
        return row;
    }

    /**
     * 复制keys
     *
     * @param apiKey
     * @return 结果
     */
    @Override
    public ApiKey copyApiKeys(ApiKey apiKey) {
        ApiKey key = apiKeyDao.checkApiName(apiKey.getApiName());
        Assert.isNullThrows(key, new ServiceException("渠道名称已存在"));

        //  生成api客户端ID
        String apiClientId = IdUtils.snowFlakeIdStr();

        ApiKey apiKeyDTO = getById(apiKey.getId());
        apiKeyDTO.setApiName(apiKey.getApiName());

        // 生成渠道
        LoanChannel loanChannel = new LoanChannel();
        loanChannel.setUniqueCode(IdUtils.getSimpleUuid());
        loanChannel.setType(3);
        loanChannel.setVersion(10);
        loanChannel.setProviderType(apiKeyDTO.getApiAccessProviderType());
        loanChannel.setHistoryUserProportion(apiKeyDTO.getApiHistoryUserProportion());
        loanChannel.setHistoryUserOrderType(apiKeyDTO.getApiHistoryUserOrderType());
        loanChannel.setName("API渠道-" + apiKeyDTO.getApiName());
        loanChannel.setCreateTime(DateUtils.getNowDate());
        loanChannel.setCreateBy(SecurityUtils.getUsername());
        loanChannelDao.insertLoanChannel(loanChannel);

        // 撞库数据查询
        ApiRequiredRule collisionFormSelected = new ApiRequiredRule();
        collisionFormSelected.setApiClientId(apiKeyDTO.getApiClientId());
        collisionFormSelected.setType(0);
        // 进件数据查询
        ApiRequiredRule enterFormSelected = new ApiRequiredRule();
        enterFormSelected.setApiClientId(apiKeyDTO.getApiClientId());
        enterFormSelected.setType(1);

        // 赋值新密钥
        apiKeyDTO.setApiClientId(apiClientId);
        apiKeyDTO.setApiSecretKey(KeyUtils.generate256ToHexStr());
        apiKeyDTO.setCreateTime(DateUtils.getNowDate());
        apiKeyDTO.setStatus(1);
        apiKeyDTO.setLoanChannelId(loanChannel.getId());
        apiKeyDao.insertApiKeys(apiKeyDTO);

        // 复制撞库数据
        List<ApiRequiredRule> collisionRuleList = apiRequiredRuleService.list(collisionFormSelected);
        if (ValidParam.isNotEmpty(collisionRuleList)) {
            IteratorUtils.array(collisionRuleList, item -> item.setApiClientId(apiClientId));
            for (ApiRequiredRule apiRequiredRule : collisionRuleList){
                apiRequiredRule.setCreateTime(DateUtils.getNowDate());
            }
            apiRequiredRuleDao.batchInsert(collisionRuleList);
        }

        // 复制进件数据
        List<ApiRequiredRule> enterList = apiRequiredRuleService.list(enterFormSelected);
        if (ValidParam.isNotEmpty(enterList)) {
            IteratorUtils.array(enterList, item -> item.setApiClientId(apiClientId));
            for (ApiRequiredRule apiRequiredRule : collisionRuleList){
                apiRequiredRule.setCreateTime(DateUtils.getNowDate());
            }
            apiRequiredRuleDao.batchInsert(enterList);
        }

        //  将API更新存入缓存
        ApiKeyCache apiKeyCache = CopyUtils.copy(apiKeyDTO, ApiKeyCache.class);
        apiKeyCacheService.set(apiKeyDTO.getApiClientId(), apiKeyCache);

        //  API规则存入缓存
        apiKeyRuleCacheService.reload(() -> {
            //  获取对应规则表单信息
            ApiRequiredRule apiRequiredRule = new ApiRequiredRule();
            apiRequiredRule.setApiClientId(apiKeyCache.getApiClientId());
            List<ApiRequiredRule> listRuleForm = apiRequiredRuleDao.listAndForm(apiRequiredRule);
            Map<Integer, List<ApiRequiredRule>> ruleTypeMap = CollUtils.groupBy(listRuleForm, t -> t.getType());
            List<ApiKeyRuleCache> listDataExchangeRule = CopyUtils.copyList(ruleTypeMap.get(0), ApiKeyRuleCache.class);
            List<ApiKeyRuleCache> listApplyRule = CopyUtils.copyList(ruleTypeMap.get(1), ApiKeyRuleCache.class);

            //  保存redis信息
            ApiKeyListRuleCache apiKeyListRuleCache = new ApiKeyListRuleCache();
            apiKeyListRuleCache.setApiClientId(apiKeyCache.getApiClientId());
            apiKeyListRuleCache.setListDataExchangeRule(listDataExchangeRule);
            apiKeyListRuleCache.setListApplyRule(listApplyRule);
            return apiKeyListRuleCache;
        });
        return apiKeyDTO;
    }

    /**
     * 修改keys
     *
     * @param reqApiKeyVO
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateApiKeys(ReqApiKeyVO reqApiKeyVO) {
        int row = 0;
        // 删除API选中进件必填项
        if (!ValidParam.isEmpty(reqApiKeyVO.getOptionsFormSelected())) {
            //选中的进件必填项ID
            List<Long> optionsFormSelected = reqApiKeyVO.getOptionsFormSelected();
            //API进件必填项集合 批量插入参数list
            List<ApiRequiredRule> apiRequiredRuleList = new ArrayList<>();
            row = apiRequiredRuleDao.batchDeleteByList(reqApiKeyVO.getApiKey().getApiClientId(), 1);

            for (Long id : optionsFormSelected) {
                ApiRequiredRule apiRequiredRule = new ApiRequiredRule();
                apiRequiredRule.setApiClientId(reqApiKeyVO.getApiKey().getApiClientId());
                apiRequiredRule.setType(1);
                apiRequiredRule.setLoanFormId(id);
                apiRequiredRule.setCreateTime(DateUtils.getNowDate());
                apiRequiredRuleList.add(apiRequiredRule);
            }
            row = apiRequiredRuleDao.batchInsert(apiRequiredRuleList);
        }

        // 修改API选中撞库必填项
        if (!ValidParam.isEmpty(reqApiKeyVO.getOptionsFormBumpSelected())) {
            //选中的撞库必填项ID
            List<Long> optionsFormBumpSelected = reqApiKeyVO.getOptionsFormBumpSelected();
            //API撞库必填项集合 批量插入参数list
            List<ApiRequiredRule> apiRequiredRuleList = new ArrayList<>();
            row = apiRequiredRuleDao.batchDeleteByList(reqApiKeyVO.getApiKey().getApiClientId(), 0);

            for (Long id : optionsFormBumpSelected) {
                ApiRequiredRule apiRequiredRule = new ApiRequiredRule();
                apiRequiredRule.setApiClientId(reqApiKeyVO.getApiKey().getApiClientId());
                apiRequiredRule.setType(0);
                apiRequiredRule.setLoanFormId(id);
                apiRequiredRule.setCreateTime(DateUtils.getNowDate());
                apiRequiredRuleList.add(apiRequiredRule);
            }
            row = apiRequiredRuleDao.batchInsert(apiRequiredRuleList);
        }

        //  修改api
        reqApiKeyVO.getApiKey().setBalance(null);
        row = apiKeyDao.updateApiKeys(reqApiKeyVO.getApiKey());
        if (row < 1) {
            throw new RuntimeException("回滚");
        }

        //  修改原有渠道id
        ApiKey apiKey = reqApiKeyVO.getApiKey();
        Long loanChannelId = apiKey.getLoanChannelId();
        if (ValidParam.isNotNull(loanChannelId)) {
            LoanChannel loanChannel = new LoanChannel();
            loanChannel.setId(loanChannelId);
            loanChannel.setProviderType(apiKey.getApiAccessProviderType());
            loanChannel.setName("API渠道-" + apiKey.getApiName());
            loanChannel.setUpdateBy(SecurityUtils.getUsername());
            loanChannel.setUpdateTime(DateUtils.getNowDate());
            row = loanChannelDao.updateLoanChannel(loanChannel);
            if (row < 1) {
                throw new RuntimeException("回滚2");
            }
        }

        //  获取最新的数据并-ApiKey缓存
        apiKeyCacheService.reload(() -> {
            ApiKey newApiKey = apiKeyDao.getById(apiKey.getId());
            ApiKeyCache apiKeyCache = CopyUtils.copy(newApiKey, ApiKeyCache.class);
            return apiKeyCache;
        });

        //  获取最新的数据并-ApiKeyRule规则缓存
        apiKeyRuleCacheService.reload(() -> {
            ApiKey newApiKey = apiKeyDao.getById(apiKey.getId());

            //  获取对应规则表单信息
            ApiRequiredRule apiRequiredRule = new ApiRequiredRule();
            apiRequiredRule.setApiClientId(newApiKey.getApiClientId());
            List<ApiRequiredRule> listRuleForm = apiRequiredRuleDao.listAndForm(apiRequiredRule);
            Map<Integer, List<ApiRequiredRule>> ruleTypeMap = CollUtils.groupBy(listRuleForm, t -> t.getType());
            List<ApiKeyRuleCache> listDataExchangeRule = CopyUtils.copyList(ruleTypeMap.get(0), ApiKeyRuleCache.class);
            List<ApiKeyRuleCache> listApplyRule = CopyUtils.copyList(ruleTypeMap.get(1), ApiKeyRuleCache.class);

            //  保存redis信息
            ApiKeyListRuleCache apiKeyListRuleCache = new ApiKeyListRuleCache();
            apiKeyListRuleCache.setApiClientId(newApiKey.getApiClientId());
            apiKeyListRuleCache.setListDataExchangeRule(listDataExchangeRule);
            apiKeyListRuleCache.setListApplyRule(listApplyRule);
            return apiKeyListRuleCache;
        });
        return row;
    }

    /**
     * 重置密钥
     */
    @Override
    public int resetApiKeys(ApiKey apiKeys) {
        //  生成api 密钥
        String apiSecretKey = KeyUtils.generate256ToHexStr();
        apiKeys.setApiSecretKey(apiSecretKey);

        //  获取最新的数据并-更新存入缓存
        apiKeyCacheService.reload(() -> {
            ApiKey newApiKey = apiKeyDao.getById(apiKeys.getId());
            ApiKeyCache apiKeyCache = CopyUtils.copy(newApiKey, ApiKeyCache.class);
            return apiKeyCache;
        });
        apiKeys.setBalance(null);
        return apiKeyDao.updateApiKeys(apiKeys);
    }

    /**
     * 查重api名称是否重复
     *
     * @param apiKeys
     * @return
     */
    @Override
    public boolean checkApiName(ApiKey apiKeys) {
        Long apiKeyId = StringUtils.isNull(apiKeys.getId()) ? -1L : apiKeys.getId();

        ApiKey apiKey = apiKeyDao.checkApiName(apiKeys.getApiName());
        if (StringUtils.isNotNull(apiKey) && apiKey.getId().longValue() != apiKeyId.longValue()) {
            return false;
        }
        return true;
    }

    /**
     * 功能描述: 根据api_client_id 获取
     *
     * @param id
     * @return ApiKey
     * @author hrj
     */
    @Override
    public ApiKey getByApiClientId(String id) {
        return apiKeyDao.getByApiClientId(id);
    }

    /**
     * 新增进件表单必填项
     *
     * @param apiClientId
     * @return
     */
    private List<ApiRequiredRule> margeRule(String apiClientId, Integer type, List<Long> optionsFormSelected) {
        //  API表单集合 批量插入参数list
        List<ApiRequiredRule> apiRequiredRuleList = new ArrayList<>();
        for (Long id : optionsFormSelected) {
            ApiRequiredRule apiRequiredRule = new ApiRequiredRule();
            apiRequiredRule.setApiClientId(apiClientId);
            apiRequiredRule.setLoanFormId(id);
            apiRequiredRule.setType(type);
            apiRequiredRule.setCreateTime(DateUtils.getNowDate());
            apiRequiredRuleList.add(apiRequiredRule);
        }
        return apiRequiredRuleList;
    }

    @Override
    public void balanceOperation(ApiKeyBalanceOperationVO balanceOperationVO) {
        ApiKey apiKey2 = apiKeyDao.getById(balanceOperationVO.getId());
        apiKeyBalanceOperationLock.executeInLock(apiKey2.getApiClientId(), () -> {
            transactionSupport.requiresNew(() -> {
                ApiKey apiKey = apiKeyDao.getById(balanceOperationVO.getId());

                // 【操作类型】
                Integer type = balanceOperationVO.getBalanceType();
                // 【操作金额】
                BigDecimal money = balanceOperationVO.getMoney();
                // 【余额】
                BigDecimal balance = apiKey.getBalance();
                // 【剩余余额】
                BigDecimal remainingBalance = BigDecimal.ZERO;

                // 计算【剩余余额】
                if (Objects.equals(type, BalanceOperationType.DEDUCT.getTypeValue())) {
                    remainingBalance = balance.subtract(money);
                } else if (Objects.equals(type, BalanceOperationType.RECHARGE.getTypeValue())) {
                    remainingBalance = balance.add(money);
                } else if (Objects.equals(type, BalanceOperationType.SUPPLEMENT.getTypeValue())) {
                    remainingBalance = balance.add(money);
                } else {
                    return;
                }

                if (Objects.equals(type, BalanceOperationType.DEDUCT.getTypeValue())) {
                    // 余额不足则不允许扣款
                    if (BigDecimalUtils.compare(remainingBalance, 0) < 0) {
                        throw new ServiceException("余额不足，请充值后重新扣款", HttpStatus.ERROR);
                    }
                }

                //  记录账单
                ApiKeyBillPO bill = new ApiKeyBillPO();
                bill.setLoanChannelId(apiKey.getLoanChannelId());
                bill.setApiClientId(apiKey.getApiClientId());
                bill.setAmount(money);
                bill.setAccountBalance(remainingBalance);
                bill.setCurrentBalance(balance);
                bill.setType(type);
                bill.setCreateBy(SecurityUtils.getUsername());
                bill.setCreateTime(new Date());
                bill.setRemark(balanceOperationVO.getRemark());
                apiKeyBillService.insertApiKeyBill(bill);

                //  修改余额
                ApiKey update = new ApiKey();
                update.setApiClientId(apiKey.getApiClientId());
                update.setBalance(remainingBalance);
                apiKeyDao.updateBalance(update);
            });
        });
    }
}
